package com.weareint.common.entity;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.weareint.common.dto.*;
import com.weareint.common.exceptions.SystemRuntimeException;
import io.vavr.Tuple;
import io.vavr.Tuple2;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 *
 *
 * <pre>
 *      {@link QueryWrapper} 与 {@link UpdateWrapper} 装饰器
 * </pre>
 *
 * @author wangg
 * @date 2021/7/1012:00
 */
public class WrapperDecorater {

    /**
     *
     *
     * <pre>
     *     修改原 {@link QueryWrapper}，在此基础上添加需要查询的列名，
     *     但是这些列名都必须通过标准数据字段名集合的校验
     * </pre>
     *
     * @param queryWrapper {@link QueryWrapper}
     * @param columns 列名
     * @param standardColumns 标准数据字段名
     * @param log {@link Logger}
     */
    public static <T> void decorateSelectForQueryWrapper(
            QueryWrapper<T> queryWrapper,
            List<String> columns,
            String[] standardColumns,
            Logger log) {
        if (null == queryWrapper) {
            throw new SystemRuntimeException("QueryWrapper not be null.");
        }
        if (null == columns
                || columns.isEmpty()
                || null == standardColumns
                || standardColumns.length == 0) {
            return;
        }
        List<String> selectColumns = new ArrayList<>();
        for (String column : columns) {
            boolean matched = false;
            if (null == column) {
                continue;
            }
            for (String tmpl : standardColumns) {
                if (null == tmpl) {
                    continue;
                }
                if (Objects.equals(column.toLowerCase(), tmpl.toLowerCase())) {
                    matched = true;
                    break;
                }
            }
            if (matched) {
                selectColumns.add(column);
            } else {
                if (null != log && log.isWarnEnabled()) {
                    log.warn("Receive invalid column, column=" + column);
                }
            }
        }
        if (!selectColumns.isEmpty()) {
            queryWrapper.select(selectColumns.toArray(new String[] {}));
        }
    }

    /**
     *
     *
     * <pre>
     *     修改原 {@link QueryWrapper}，在此基础上增加排序字段，
     *     但是每个前台传入的字段都需要经过检测
     * </pre>
     *
     * @param queryWrapper {@link QueryWrapper}
     * @param orderByProps {@link QueryByPropsWithOprDto.OrderByProp}
     * @param standardColumns 标准数据字段名
     * @param log {@link Logger}
     */
    public static <T> void decorateOrderByForQueryWrapper(
            QueryWrapper<T> queryWrapper,
            List<QueryByPropsWithOprDto.OrderByProp> orderByProps,
            String[] standardColumns,
            Logger log) {
        if (null == queryWrapper) {
            throw new SystemRuntimeException("QueryWrapper not be null.");
        }
        if (null == orderByProps || orderByProps.isEmpty()) {
            return;
        }
        List<Tuple2<String, Boolean>> tuple2s = new ArrayList<>();
        for (QueryByPropsWithOprDto.OrderByProp orderByProp : orderByProps) {
            if (null == orderByProp) {
                continue;
            }
            String column = checkAndGetRealColumn(orderByProp.getProp(), standardColumns);
            if (null == column) {
                if (null != log && log.isErrorEnabled()) {
                    log.error("Receive invalid column, column=" + orderByProp.getProp());
                }
                continue;
            }
            tuple2s.add(Tuple.of(column, orderByProp.isDesc()));
        }
        tuple2s.forEach(
                it -> {
                    if (it._2) {
                        queryWrapper.orderByDesc(it._1);
                    } else {
                        queryWrapper.orderByAsc(it._1);
                    }
                });
    }

    /**
     *
     *
     * <pre>
     *     修改原 {@link AbstractWrapper},在此基础添加Where条件。
     *     每个前台传入的参数都需检验
     * </pre>
     *
     * @param wrapper {@link AbstractWrapper}
     * @param wherePropsWithOprs {@link WherePropsWithOpr}
     * @param standardColumns 标准数据字段名
     * @param log {@link Logger}
     * @param <M> {@link M}
     */
    public static <T, M extends AbstractWrapper<T, String, M>> void decorateWhereForWrapper(
            AbstractWrapper<T, String, M> wrapper,
            List<WherePropsWithOpr> wherePropsWithOprs,
            String[] standardColumns,
            Logger log) {
        if (null == wrapper) {
            throw new SystemRuntimeException("UpdateWrapper not be null.");
        }
        if (null == wherePropsWithOprs || wherePropsWithOprs.isEmpty()) {
            return;
        }
        for (WherePropsWithOpr wherePropsWithOpr : wherePropsWithOprs) {
            String opr = wherePropsWithOpr.getOpr();
            String column = checkAndGetRealColumn(wherePropsWithOpr.getProp(), standardColumns);
            if (null == column) {
                if (null != log && log.isErrorEnabled()) {
                    log.error("Receive invalid column, column=" + wherePropsWithOpr.getProp());
                }
                continue;
            }
            Oprs.Opr oprEnum = Oprs.getOprEnum(opr);
            if (null == oprEnum) {
                if (null != log && log.isErrorEnabled()) {
                    log.error("Not Found Opr Enum for " + opr);
                }
            } else {
                oprEnum.decorate(
                        wrapper, wherePropsWithOpr.getType(), column, wherePropsWithOpr.getVal());
            }
        }
    }

    /**
     *
     *
     * <pre>
     *      修改原 {@link UpdateWrapper}，在此基础上添加Set字段
     * </pre>
     *
     * @param updateWrapper {@link UpdateWrapper}
     * @param updateProps {@link UpdateByPropsWithOprDto.UpdateProp}
     * @param standardColumns 标准数据字段名
     * @param log {@link Logger}
     */
    public static <T> void decorateSetForUpdateWrapper(
            UpdateWrapper<T> updateWrapper,
            List<UpdateByPropsWithOprDto.UpdateProp> updateProps,
            String[] standardColumns,
            Logger log) {
        if (null == updateWrapper) {
            return;
        }
        if (null == updateProps || updateProps.isEmpty()) {
            return;
        }
        for (UpdateByPropsWithOprDto.UpdateProp updateProp : updateProps) {
            String column = checkAndGetRealColumn(updateProp.getProp(), standardColumns);
            if (null == column) {
                if (null != log && log.isErrorEnabled()) {
                    log.error("Receive invalid column, column=" + updateProp.getProp());
                }
                continue;
            }
            Types.Type typeEnum = Types.getTypeEnum(updateProp.getType());
            if (null == typeEnum) {
                if (null != log && log.isErrorEnabled()) {
                    log.error("Not Found Opr Enum for " + updateProp.getType());
                }
            } else {
                Object value = typeEnum.cast(updateProp.getVal());
                updateWrapper.set(column, value);
            }
        }
    }

    /**
     *
     *
     * <pre>
     *     把传入的属性名与表字段名进行匹配，只能匹配上的才可进入下一环节
     * </pre>
     *
     * @param prop 外界传入的属性名
     * @param standardColumns 标准数据字段名
     * @return 真实的字段名
     */
    private static String checkAndGetRealColumn(String prop, String[] standardColumns) {
        if (null == prop) {
            return null;
        }
        for (String column : standardColumns) {
            if (null != column) {
                if (Objects.equals(prop.toUpperCase(), column.toUpperCase())) {
                    return column;
                }
            }
        }
        return null;
    }
}
